<!DOCTYPE html>
            
<HTML>
<HEAD>
<meta name="booktitle" content="Developing Applications With Objective Caml" >
 <meta charset="ISO-8859-1"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<META name="GENERATOR" content="hevea 1.05-7 of 2000-02-24">
<META NAME="Author" CONTENT="Christian.Queinnec@lip6.fr">
<LINK rel=stylesheet type="text/css" href="videoc-ocda.css">
<script language="JavaScript" src="videoc.js"><!--
//--></script>
<TITLE>
 Client-server Toolbox
</TITLE>
</HEAD>
<BODY class="regularBody">
<A HREF="book-ora193.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora195.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
<HR>

<H2> Client-server Toolbox</H2>
<A NAME="sec-protocole"></A>We present a collection of modules to enable client-server interactions among
Objective CAML programs. This toolbox will be used in the two applications that
follow.<BR>
<BR>
A client-server application differs from others in the protocol that it uses
and in the processing that it associates with the protocol. Otherwise, all
such applications use very similar mechanisms: waiting for a connection,
starting a separate process to handle the connection, and reading and writing
sockets.<BR>
<BR>
Taking advantage of Objective CAML's ability to combine modular genericity and
extension of objects, we will create a collection of functors which take as
argument a communications protocol and produce generic classes implementing
the mechanisms of clients and of servers. We can then subclass these to obtain
the particular processing we need.<BR>
<BR>
<A NAME="toc289"></A>
<H3> Protocols</H3>A communications protocol is a type of data that can be translated into a
sequence of characters and transmitted from one machine to another via a
socket. This can be described using a signature.


<PRE><BR># <B>module</B><CODE> </CODE><B>type</B><CODE> </CODE>PROTOCOL<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>sig</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>type</B><CODE> </CODE>t<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>to_string<CODE> </CODE><CODE>:</CODE><CODE> </CODE>t<CODE> </CODE>-&gt;<CODE> </CODE>string<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>of_string<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string<CODE> </CODE>-&gt;<CODE> </CODE>t<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
The signature requires that the data type be monomorphic; yet we can choose
a data type as complex as we wish, as long as we can translate it to a
sequence of characters and back. In particular, nothing prevents us from using
objects as our data.


<PRE><BR># <B>module</B><CODE> </CODE>Integer<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>struct</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>class</B><CODE> </CODE>integer<CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>object</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>v<CODE> </CODE><CODE>=</CODE><CODE> </CODE>x<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE>v<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>str<CODE> </CODE><CODE>=</CODE><CODE> </CODE>string_of_int<CODE> </CODE>v<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>type</B><CODE> </CODE>t<CODE> </CODE><CODE>=</CODE><CODE> </CODE>integer<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>to_string<CODE> </CODE>o<CODE> </CODE><CODE>=</CODE><CODE> </CODE>o#str<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>of_string<CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>new</B><CODE> </CODE>integer<CODE> </CODE><TT>(</TT>int_of_string<CODE> </CODE>s<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
By making some restrictions on the types of data to be manipulated, we can use
the module <TT>Marshal</TT>, described on page <A HREF="book-ora076.html#mod-marshal">??</A>, to
define the translation functions.


<PRE><BR># <B>module</B><CODE> </CODE>Make_Protocol<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>functor</B><CODE> </CODE><TT>(</TT><CODE> </CODE>T<CODE> </CODE><CODE>:</CODE><CODE> </CODE><B>sig</B><CODE> </CODE><B>type</B><CODE> </CODE>t<CODE> </CODE><B>end</B><CODE> </CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>struct</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>type</B><CODE> </CODE>t<CODE> </CODE><CODE>=</CODE><CODE> </CODE>T.t<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>to_string<CODE> </CODE><TT>(</TT>x<CODE>:</CODE>t<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>Marshal.to_string<CODE> </CODE>x<CODE> </CODE><CODE>[</CODE>Marshal<CODE>.</CODE>Closures<CODE>]</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>of_string<CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>Marshal.from_string<CODE> </CODE>s<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>:</CODE><CODE> </CODE>t<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
<A NAME="toc290"></A>
<H3> Communication</H3>Since a protocol is a type of value that can be translated into a sequence
of characters, we can make these values persistent and store them in a file.<BR>
<BR>
The only difficulty in reading such a value from a file when we do not know
its type is that a priori we do not know the size of the data in
question. And since the file in question is in fact a socket, we cannot
simply check an end of file marker. To solve this problem,
we will write the size of the data, as a number of characters, before the data
itself. The first twelve characters will contain the size, padded with spaces.<BR>
<BR>
The functor <TT>Com</TT> takes as its parameter a module with signature
<TT>PROTOCOL</TT> and defines the functions for transmitting and receiving
values encoded using the protocol.<BR>
<BR>


<PRE><BR># <B>module</B><CODE> </CODE>Com<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>functor</B><CODE> </CODE><TT>(</TT>P<CODE> </CODE><CODE>:</CODE><CODE> </CODE>PROTOCOL<TT>)</TT><CODE> </CODE>-&gt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>struct</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>send<CODE> </CODE>fd<CODE> </CODE>m<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>mes<CODE> </CODE><CODE>=</CODE><CODE> </CODE>P.to_string<CODE> </CODE>m<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>string_of_int<CODE> </CODE><TT>(</TT>String.length<CODE> </CODE>mes<TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>buffer<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.make<CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE> </CODE><CODE>' '</CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>for</B><CODE> </CODE>i<CODE>=</CODE><CODE>0</CODE><CODE> </CODE><B>to</B><CODE> </CODE><TT>(</TT>String.length<CODE> </CODE>l<TT>)</TT><CODE>-</CODE><CODE>1</CODE><CODE> </CODE><B>do</B><CODE> </CODE>buffer<CODE>.[</CODE>i<CODE>]</CODE><CODE> </CODE><CODE>&lt;-</CODE><CODE> </CODE>l<CODE>.[</CODE>i<CODE>]</CODE><CODE> </CODE><B>done</B><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>ThreadUnix.write<CODE> </CODE>fd<CODE> </CODE>buffer<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>ThreadUnix.write<CODE> </CODE>fd<CODE> </CODE>mes<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT>String.length<CODE> </CODE>mes<TT>)</TT><TT>)</TT><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>receive<CODE> </CODE>fd<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>buffer<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.make<CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE> </CODE><CODE>' '</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>ThreadUnix.read<CODE> </CODE>fd<CODE> </CODE>buffer<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>let</B><CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>while</B><CODE> </CODE><TT>(</TT>buffer<CODE>.[!</CODE>i<CODE>]&lt;&gt;</CODE><CODE>' '</CODE><TT>)</TT><CODE> </CODE><B>do</B><CODE> </CODE>incr<CODE> </CODE>i<CODE> </CODE><B>done</B><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>int_of_string<CODE> </CODE><TT>(</TT>String.sub<CODE> </CODE>buffer<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>!</CODE>i<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>buffer<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.create<CODE> </CODE>l<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>ThreadUnix.read<CODE> </CODE>fd<CODE> </CODE>buffer<CODE> </CODE><CODE>0</CODE><CODE> </CODE>l<TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>P.of_string<CODE> </CODE>buffer<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR><CODE>module Com :</CODE><BR><CODE>  functor(P : PROTOCOL) -&gt;</CODE><BR><CODE>    sig</CODE><BR><CODE>      val send : Unix.file_descr -&gt; P.t -&gt; unit</CODE><BR><CODE>      val receive : Unix.file_descr -&gt; P.t</CODE><BR><CODE>    end</CODE><BR>

</PRE>

Note that we use the functions <TT>read</TT> and <TT>write</TT> from
module <TT>ThreadUnix</TT> and not those from module <TT>Unix</TT>;
this will permit us to use our functions in a thread without blocking the
execution of other processes.<BR>
<BR>
<A NAME="toc291"></A>
<H3> Server</H3>A server is built as an abstract class parameterized by the type of data in
the protocol. Its constructor takes as arguments a port number and the
maximum number of simultaneous connections allowed. The method for processing
a request is abstract; it must be implemented in a subclass of <TT>server</TT>
to obtain a concrete class.<BR>
<BR>


<PRE><BR># <B>module</B><CODE> </CODE>Server<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>functor</B><CODE> </CODE><TT>(</TT>P<CODE> </CODE><CODE>:</CODE><CODE> </CODE>PROTOCOL<TT>)</TT><CODE> </CODE>-&gt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>struct</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>module</B><CODE> </CODE>Com<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Com<CODE> </CODE><TT>(</TT>P<TT>)</TT><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>class</B><CODE> </CODE><B>virtual</B><CODE> </CODE><CODE>[</CODE><I>'a</I><CODE>]</CODE><CODE> </CODE>server<CODE> </CODE>p<CODE> </CODE>np<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>object</B><CODE> </CODE><TT>(</TT>s<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>constraint</B><CODE> </CODE><I>'a</I><CODE> </CODE><CODE>=</CODE><CODE> </CODE>P.t<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>port_num<CODE> </CODE><CODE>=</CODE><CODE> </CODE>p<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>nb_pending<CODE> </CODE><CODE>=</CODE><CODE> </CODE>np<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>val</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ThreadUnix.socket<CODE> </CODE>Unix<CODE>.</CODE>PF_INET<CODE> </CODE>Unix<CODE>.</CODE>SOCK_STREAM<CODE> </CODE><CODE>0</CODE><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>start<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>host<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix.gethostbyname<CODE> </CODE><TT>(</TT>Unix.gethostname()<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>h_addr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>host<CODE>.</CODE>Unix.h_addr_list<CODE>.</CODE><TT>(</TT><CODE>0</CODE><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock_addr<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>h_addr<CODE>,</CODE><CODE> </CODE>port_num<TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.bind<CODE> </CODE>sock<CODE> </CODE>sock_addr<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.listen<CODE> </CODE>sock<CODE> </CODE>nb_pending<CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE><B>true</B><CODE> </CODE><B>do</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><TT>(</TT>service_sock<CODE>,</CODE><CODE> </CODE>client_sock_addr<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>ThreadUnix.accept<CODE> </CODE>sock<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>ignore<CODE> </CODE><TT>(</TT>Thread.create<CODE> </CODE>s#process<CODE> </CODE>service_sock<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>send<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Com.send<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>receive<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Com.receive<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE><B>virtual</B><CODE> </CODE>process<CODE> </CODE><CODE>:</CODE><CODE> </CODE>Unix.file_descr<CODE> </CODE>-&gt;<CODE> </CODE>unit<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR>

</PRE>
<BR>
<BR>
In order to show these ideas in use, let us revisit the capital service,
adding the capability of sending lists of strings.


<PRE><BR># <B>type</B><CODE> </CODE>message<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Str<CODE> </CODE><B>of</B><CODE> </CODE>string<CODE> </CODE><CODE>|</CODE><CODE> </CODE>LStr<CODE> </CODE><B>of</B><CODE> </CODE>string<CODE> </CODE>list<CODE> </CODE>;;<BR># <B>module</B><CODE> </CODE>Cap_Protocol<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Make_Protocol<CODE> </CODE><TT>(</TT><B>struct</B><CODE> </CODE><B>type</B><CODE> </CODE>t<CODE>=</CODE>message<CODE> </CODE><B>end</B><TT>)</TT><CODE> </CODE>;;<BR># <B>module</B><CODE> </CODE>Cap_Server<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Server<CODE> </CODE><TT>(</TT>Cap_Protocol<TT>)</TT><CODE> </CODE>;;<BR>

</PRE>



<PRE><BR># <B>class</B><CODE> </CODE>cap_server<CODE> </CODE>p<CODE> </CODE>np<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>object</B><CODE> </CODE><TT>(</TT>self<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>inherit</B><CODE> </CODE><CODE>[</CODE>message<CODE>]</CODE><CODE> </CODE>Cap_Server.server<CODE> </CODE>p<CODE> </CODE>np<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>method</B><CODE> </CODE>process<CODE> </CODE>fd<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>self#receive<CODE> </CODE>fd<CODE> </CODE><B>with</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Str<CODE> </CODE>s<CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>self#send<CODE> </CODE>fd<CODE> </CODE><TT>(</TT>Str<CODE> </CODE><TT>(</TT>String.uppercase<CODE> </CODE>s<TT>)</TT><TT>)</TT><CODE> </CODE>;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.close<CODE> </CODE>fd<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>LStr<CODE> </CODE>l<CODE> </CODE>-&gt;<CODE> </CODE>self#send<CODE> </CODE>fd<CODE> </CODE><TT>(</TT>LStr<CODE> </CODE><TT>(</TT>List.map<CODE> </CODE>String.uppercase<CODE> </CODE>l<TT>)</TT><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>Unix.close<CODE> </CODE>fd<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR><CODE>class cap_server :</CODE><BR><CODE>  int -&gt;</CODE><BR><CODE>  int -&gt;</CODE><BR><CODE>  object</CODE><BR><CODE>    val nb_pending : int</CODE><BR><CODE>    val port_num : int</CODE><BR><CODE>    val sock : Unix.file_descr</CODE><BR><CODE>    method process : Unix.file_descr -&gt; unit</CODE><BR><CODE>    method receive : Unix.file_descr -&gt; Cap_Protocol.t</CODE><BR><CODE>    method send : Unix.file_descr -&gt; Cap_Protocol.t -&gt; unit</CODE><BR><CODE>    method start : unit</CODE><BR><CODE>  end</CODE><BR>

</PRE>
<BR>
<BR>
The processing consists of receiving a request, examining it, processing it
and sending the result. The functor allows us to concentrate on this
processing while constructing the server; the rest is generic. However, if
we wanted a different mechanism, such as for example using acknowledgements,
nothing would prevent us from redefining the inherited methods for
communication.<BR>
<BR>
<A NAME="toc292"></A>
<H3> Client</H3>To construct clients using a given protocol, we define three general-purpose
functions:
<UL>
<LI>
 <TT>connect</TT>: establishes a connection with a server; it takes
the address (IP address and port number) and returns a file descriptor
corresponding to a socket connected to the server.

<LI> <TT>emit_simple</TT>: opens a connection, sends a message and closes
the connection.

<LI> <TT>emit_answer</TT>: same as <TT>emit_simple</TT>, but waits for the server's
response before closing the connection.
</UL>


<PRE><BR># <B>module</B><CODE> </CODE>Client<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>functor</B><CODE> </CODE><TT>(</TT>P<CODE> </CODE><CODE>:</CODE><CODE> </CODE>PROTOCOL<TT>)</TT><CODE> </CODE>-&gt;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>struct</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>module</B><CODE> </CODE>Com<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Com<CODE> </CODE><TT>(</TT>P<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>connect<CODE> </CODE>addr<CODE> </CODE>port<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ThreadUnix.socket<CODE> </CODE>Unix<CODE>.</CODE>PF_INET<CODE> </CODE>Unix<CODE>.</CODE>SOCK_STREAM<CODE> </CODE><CODE>0</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>and</B><CODE> </CODE>in_addr<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>Unix.gethostbyname<CODE> </CODE>addr<TT>)</TT><CODE>.</CODE>Unix.h_addr_list<CODE>.</CODE><TT>(</TT><CODE>0</CODE><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>ThreadUnix.connect<CODE> </CODE>sock<CODE> </CODE><TT>(</TT>Unix<CODE>.</CODE>ADDR_INET<TT>(</TT>in_addr<CODE>,</CODE><CODE> </CODE>port<TT>)</TT><TT>)</TT><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>sock<CODE> </CODE><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>emit_simple<CODE> </CODE>addr<CODE> </CODE>port<CODE> </CODE>mes<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>connect<CODE> </CODE>addr<CODE> </CODE>port<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Com.send<CODE> </CODE>sock<CODE> </CODE>mes<CODE> </CODE>;<CODE> </CODE><CODE> </CODE>Unix.close<CODE> </CODE>sock<CODE> </CODE><BR><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>emit_answer<CODE> </CODE>addr<CODE> </CODE>port<CODE> </CODE>mes<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>sock<CODE> </CODE><CODE>=</CODE><CODE> </CODE>connect<CODE> </CODE>addr<CODE> </CODE>port<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Com.send<CODE> </CODE>sock<CODE> </CODE>mes<CODE> </CODE>;<CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>res<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Com.receive<CODE> </CODE>sock<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE>Unix.close<CODE> </CODE>sock<CODE> </CODE>;<CODE> </CODE>res<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>end</B><CODE> </CODE>;;<BR><CODE>module Client :</CODE><BR><CODE>  functor(P : PROTOCOL) -&gt;</CODE><BR><CODE>    sig</CODE><BR><CODE>      module Com :</CODE><BR><CODE>        sig</CODE><BR><CODE>          val send : Unix.file_descr -&gt; P.t -&gt; unit</CODE><BR><CODE>          val receive : Unix.file_descr -&gt; P.t</CODE><BR><CODE>        end</CODE><BR><CODE>      val connect : string -&gt; int -&gt; Unix.file_descr</CODE><BR><CODE>      val emit_simple : string -&gt; int -&gt; P.t -&gt; unit</CODE><BR><CODE>      val emit_answer : string -&gt; int -&gt; P.t -&gt; P.t</CODE><BR><CODE>    end</CODE><BR>

</PRE>

The last two functions are of a higher level than the first: the mechanism
linking the client and the server does not appear. The caller of
<TT>emit_answer</TT> does not even need to know that the computation it is
requesting is carried out by a remote machine. As far as the caller is
concerned, it invokes a function that is represented by an address and port,
with an argument which is the message to be sent, and a value is returned to
it. The distributed aspect can seem entirely hypothetical.<BR>
<BR>
A client of the capital service is extremely easy to construct. Assume
that the <TT>boulmich</TT> machine provides the service on port number 12345;
then the function <TT>list_uppercase</TT> can be defined by means of a call
to the service.


<PRE><BR># <B>let</B><CODE> </CODE>list_uppercase<CODE> </CODE>l<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><B>module</B><CODE> </CODE>Cap_client<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Client<CODE> </CODE><TT>(</TT>Cap_Protocol<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>match</B><CODE> </CODE>Cap_client.emit_answer<CODE> </CODE><CODE>"boulmich"</CODE><CODE> </CODE><CODE>1</CODE><CODE>2</CODE><CODE>3</CODE><CODE>4</CODE><CODE>5</CODE><CODE> </CODE><TT>(</TT>LStr<CODE> </CODE>l<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Str<CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE><CODE>[</CODE>x<CODE>]</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>LStr<CODE> </CODE>x<CODE> </CODE>-&gt;<CODE> </CODE>x<CODE> </CODE>;;<BR><CODE>val list_uppercase : string list -&gt; string list = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="toc293"></A>
<H3> To Learn More</H3>The first improvement to be made to our toolbox is some error handling,
which has been totally absent so far. Recovery from exceptions which arise
from a broken connection, and a mechanism for retrying, would be most welcome.<BR>
<BR>
In the same vein, the client and the server would benefit from a timeout
mechanism which would make it possible to limit the time to wait for a
response.<BR>
<BR>
Because we have constructed the generic server as a class, which moreover is
parameterized by the type of data to be transmitted over the network, it is
easy to extend it to augment or modify its behavior in order to implement any
desired improvements.<BR>
<BR>
Another approach is to enrich the communication protocols. One can for example
add requests for acknowledgement to the protocol, or accompany each request by
a checksum allowing verification that the network has not corrupted the data.


<HR>
<A HREF="book-ora193.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora195.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
</BODY>
</HTML>
